#include "ezqaudioplayer.h"
#include "decode/libavdecode.h"
#include "stream/streambase.h"
#include "log/EZQLOG.h"
#include <QFile>
#include <QApplication>


EZQAudioPlayer* EZQAudioPlayer::sharePlayer()
{
    static EZQAudioPlayer *player = 0;
    if(player==0){
        qRegisterMetaType<EZQAudioPlayStatus>();
        player = new EZQAudioPlayer();
    }
    return player;
}

EZQAudioPlayer::EZQAudioPlayer(QObject *parent) :
    QIODevice(parent)
{
    _decode = 0;
    _stream = 0;
    _player = 0;
    _pcmBuffer = 0;
    _readPcmOil = 0;
    _writePcmOil = 0;
    _pcmBufferSize = 0;
    _currentTime = 0;
    _duration = 0;
    setOpenMode(QIODevice::ReadOnly);
    _thread = new QThread();
    this->moveToThread(_thread);
    _thread->start();
    _willPlay = false;
    setVolume(1);

    connect(this,SIGNAL(sigReadDataEnd()),this,SLOT(slotFillBuf()),Qt::QueuedConnection);
    _decode = new LibavDecode();
    connect(_decode,SIGNAL(sidecodePcmAvailableChange()),this,SLOT(onDecodePcmAvailableChange()),Qt::QueuedConnection);
    connect(_decode,SIGNAL(sidecodeStateChange(LibavStatus)),this,SLOT(onDecodeStateChange(LibavStatus)),Qt::QueuedConnection);
}
StreamBase *EZQAudioPlayer::stream() const
{
    return _stream;
}

void EZQAudioPlayer::play()
{
    QMutexLocker loker(&_decodeMutex);
    QMutexLocker lokerStatus(&_statusMutex);
    QMutexLocker lockerPlayer(&_playerMutex);

    _willPlay = true;
    _play();
}

void EZQAudioPlayer::playOrPause()
{
    QMutexLocker lockerPlayer(&_playerMutex);
    QMutexLocker lokerStatus(&_statusMutex);
    if(_willPlay == true){
        _willPlay = false;
        _pause();
    }else{
        _willPlay = true;
        _play();
    }
}

void EZQAudioPlayer::pause()
{
//    QMutexLocker locKerStatus(&_statusMutex);
    QMutexLocker lokerStatus(&_statusMutex);
    QMutexLocker lockerPlayer(&_playerMutex);
    _willPlay = false;
    _pause();
}

void EZQAudioPlayer::stop()
{
    QMutexLocker lokerStatus(&_statusMutex);
    QMutexLocker lockerPlayer(&_playerMutex);
    _willPlay = false;
    _stop();
}
void EZQAudioPlayer::_play()
{
    if(_status == EZQAudioPlayStatus_playing){
        //already in playing
    }else if(_status == EZQAudioPlayStatus_init){
        //cant not play
    }else if(_status == EZQAudioPlayStatus_preStart){
        // in decode probe
    }else if(_status == EZQAudioPlayStatus_preIng){

    }else if(_status == EZQAudioPlayStatus_preEnd){

    }else if(_status == EZQAudioPlayStatus_bufStart){

    }else if(_status == EZQAudioPlayStatus_bufIng){

    }else if(_status == EZQAudioPlayStatus_bufEnd){
        //buff is enough go real play
        setStatus(EZQAudioPlayStatus_playing);
        if(_player->state() == QAudio::SuspendedState){
            _player->resume();
        }else{
            _player->start(this);
        }
    }else if(_status == EZQAudioPlayStatus_pause){
        // on pause
        setStatus(EZQAudioPlayStatus_playing);
        if(_player->state() == QAudio::SuspendedState){
            _player->resume();
        }else{
            _player->start(this);
        }
    }else if(_status == EZQAudioPlayStatus_stop){
        // play is stop so  undecode  start decode first
        _decode->decode(false);
    }else if(_status == EZQAudioPlayStatus_finish){
        // play is stop so  undecode  start decode first
        _decode->decode(false);
    }else if(_status == EZQAudioPlayStatus_error){
        // play is erro start decode again
        _decode->decode(false);
    }
}

//void EZQAudioPlayer::_playOrPause()
//{
//    if(_state.playState == EZVoicePlayerState_plyaing){
//        _pause();
//    }else{
//        _play();
//    }
//}

void EZQAudioPlayer::_pause(bool changestatus)
{
    if(_status == EZQAudioPlayStatus_playing){
        _player->suspend();
        if(changestatus){
            setStatus(EZQAudioPlayStatus_pause);
        }
    }
}

void EZQAudioPlayer::_stop(bool changestatus)
{
    if(_status == EZQAudioPlayStatus_playing || _status == EZQAudioPlayStatus_pause){
        _player->stop();
        if(changestatus){
            setStatus(EZQAudioPlayStatus_stop);
        }

    }
    _currentTime = 0;
}
void EZQAudioPlayer::setStream(StreamBase *stream)
{
//    cleanPlayer();
    QMutexLocker lockerDecode(&_decodeMutex);

    if(_decode){
//        disconnect(_decode,0,this,0);
//        _decode->stopDecode();
//        _decode->Release();
//        _decode = 0;
        _decode->stopDecode();
    }
    QMutexLocker locker(&_statusMutex);
    _stop();
    setStatus(EZQAudioPlayStatus_preStart);

    if(_stream){
        _stream->Release();
        disconnect(_stream,SIGNAL(availableChange(qint64,qint64)),this,SLOT(onStreamAvailableChange(qint64,qint64)));
    }
    if(stream){
        stream->Retain();
        connect(stream,SIGNAL(availableChange(qint64,qint64)),this,SLOT(onStreamAvailableChange(qint64,qint64)));
    }
    _stream = stream;

    _decode->setStream(stream);
    _decode->decode(false); //
}

void EZQAudioPlayer::onStreamAvailableChange(qint64 /*offset*/, qint64 /*dataLen*/)
{

}


void EZQAudioPlayer::onDecodeStateChange(LibavStatus status)
{

    QMutexLocker lockerDecode(&_decodeMutex);
    QMutexLocker lockerStatus(&_statusMutex);

    LibavDecode *decode = (LibavDecode*)sender();
    if(decode != _decode){
        return;
    }
//    EZDebug << decode << status.parentStatus << status.subStatus;
    switch (status.parentStatus) {
    case LibavDecodeStatus_init:
    {
    }
        break;
    case LibavDecodeStatus_probe:
    {
        if(status.subStatus == LibavDecodeStatus_probe_ing){
           setStatus(EZQAudioPlayStatus_preIng);
        }else if(status.subStatus == LibavDecodeStatus_probe_finish){
            //to do init player
//            QAudioOutput
            LibavAutioFormat laf = decode->audioFormat();
            QAudioFormat format;
            format.setSampleRate(laf.sample_rate);
            format.setChannelCount(laf.channels);
            format.setSampleSize(laf.sampleSize);

            format.setCodec("audio/pcm");
            format.setByteOrder(QAudioFormat::LittleEndian);
            format.setSampleType(QAudioFormat::SignedInt);

            QAudioDeviceInfo info(QAudioDeviceInfo::defaultOutputDevice());
            if (!info.isFormatSupported(format)) {
                EZDebug << "Raw audio format not supported by backend, cannot play audio.";
                setStatus(EZQAudioPlayStatus_error);
                return;
            }
            setStatus(EZQAudioPlayStatus_preEnd);
            _duration = _decode->duration();
            initPlayer(format);

        }
    }
        break;
    case LibavDecodeStatus_decode:
    {
        if(status.subStatus == LibavDecodeStatus_decode_start){
            setStatus(EZQAudioPlayStatus_bufStart);
        }else if(status.subStatus == LibavDecodeStatus_decodes_ing){

        }else if(status.subStatus == LibavDecodeStatus_decodes_stop){

        }
    }
        break;
    case LibavDecodeStatus_fail:
    {
    }
        break;
    case LibavDecodeStatus_finish:
    {

    }
         break;
    case LibavDecodeStatus_cancel:
    {
    }
        break;
    default:
        break;
    }

}

void EZQAudioPlayer::cleanPlayer()
{
    if(_player){
        _stop();
        disconnect(_player);
        delete _player;
        _player = 0;
    }
    if(_pcmBuffer){
        delete _pcmBuffer;
    }
    _pcmBuffer = 0;
    _readPcmOil = 0;
    _writePcmOil = 0;
    _pcmBufferSize = 0;

}

void EZQAudioPlayer::initPlayer(const QAudioFormat &format)
{
//    if()

//    QMutexLocker lockerStatus(&_statusMutex);
    QMutexLocker lockerPlayer(&_playerMutex);
    cleanPlayer();

    _pcmBufferMutex.lock();

    _pcmBufferSize = 3*(format.sampleSize()/8)*format.sampleRate()*format.channelCount();
    _pcmBuffer = new unsigned char[_pcmBufferSize];
    _readPcmOil = 0;
    _writePcmOil = 0;

    _pcmBufferMutex.unlock();

    _player = new QAudioOutput(format);
    _player->setVolume(_volume);
    connect(_player,SIGNAL(stateChanged(QAudio::State)),this,SLOT(onAOStateChange(QAudio::State)));
    EZDebug << "init player finish";
}
void EZQAudioPlayer::onAOStateChange(QAudio::State state)
{
    EZDebug << "onAOStateChange" << state;
    if(state == QAudio::IdleState){

        QMutexLocker lockerDecode(&_decodeMutex);
        LibavStatus decodeStatus = _decode->decodeStatus();
        if(decodeStatus.parentStatus != LibavDecodeStatus_decode){
            if(decodeStatus.parentStatus == LibavDecodeStatus_fail){
                _stop(false);
                setStatus(EZQAudioPlayStatus_error);
            }else if(decodeStatus.parentStatus == LibavDecodeStatus_finish){
                _stop(false);
                setStatus(EZQAudioPlayStatus_finish);
            }else if(decodeStatus.parentStatus == LibavDecodeStatus_cancel){
                _stop();
            }
        }else{
            _pause(false);
            setStatus(EZQAudioPlayStatus_bufStart);
        }
    }
}
void EZQAudioPlayer::onDecodePcmAvailableChange()
{
//    emit sigReadDataEnd();
    fillBuf();
}
void EZQAudioPlayer::fillBuf()
{

    _pcmBufferMutex.lock();
//    EZDebug << "fillBuf " << _writePcmOil << _readPcmOil;
    if(_pcmBuffer == 0){
        _pcmBufferMutex.unlock();
        return;
    }
    int freeSize = _pcmBufferSize - (_writePcmOil - _readPcmOil);
    if(freeSize==0){
        _pcmBufferMutex.unlock();
        return;
    }
    int hasSize = _decode->pcmBufSize();
    int fillSize = freeSize > hasSize ? hasSize:freeSize;

    int writePoint = _writePcmOil%_pcmBufferSize;

    int fill1 = writePoint + fillSize > _pcmBufferSize? _pcmBufferSize-writePoint:fillSize;
    _decode->readPcmBuf(_pcmBuffer+writePoint,fill1);
    if(fill1 < fillSize){
        int fill2 = fillSize - fill1;
        _decode->readPcmBuf(_pcmBuffer,fill2);
    }
    _writePcmOil += fillSize;


    int nowPcmLen = _writePcmOil - _readPcmOil;
//    EZDebug << "player nowpcmLen" << nowPcmLen << "buffer size"<< _pcmBufferSize;
    _pcmBufferMutex.unlock();

    QMutexLocker lockerDecode(&_decodeMutex);
    QMutexLocker lockerStatus(&_statusMutex);
    QMutexLocker lockerPlayer(&_playerMutex);
//    _decodeMutex.lock();
//    _statusMutex.lock();
//    bool islock = true;
    if(nowPcmLen == _pcmBufferSize){
        if(_status == EZQAudioPlayStatus_bufIng || _status == EZQAudioPlayStatus_bufStart ){
            setStatus(EZQAudioPlayStatus_bufEnd);
        }
        if(_willPlay==true && _status != EZQAudioPlayStatus_playing){
//            islock = false;
//            _decodeMutex.unlock();
//            _statusMutex.unlock();
            _play();
        }
    }else if(nowPcmLen < _pcmBufferSize){
        LibavStatus decodeStatus = _decode->decodeStatus();

        if(_status == EZQAudioPlayStatus_bufIng || _status == EZQAudioPlayStatus_bufStart ){
            //now is ing bufing so play error
            if(decodeStatus.parentStatus == LibavDecodeStatus_fail ||
                    decodeStatus.parentStatus == LibavDecodeStatus_finish){
                //no more pcm data can use
                setStatus(EZQAudioPlayStatus_error);
            }else if(decodeStatus.parentStatus == LibavDecodeStatus_cancel){
                // user cancel decode
                setStatus(EZQAudioPlayStatus_stop);
            }else{
               //
                setStatus(EZQAudioPlayStatus_bufIng);
            }

        }
    }
//    if(islock){
//        _decodeMutex.unlock();
//        _statusMutex.unlock();
//    }

}
void EZQAudioPlayer::slotFillBuf()
{
    fillBuf();
}
qint64 EZQAudioPlayer::readData(char *data, qint64 maxlen)
{

    _pcmBufferMutex.lock();
//    EZDebug << "readData " << _writePcmOil << _readPcmOil;
    int hasSize = _writePcmOil - _readPcmOil;
    if(hasSize == 0){
        _pcmBufferMutex.unlock();
//        emit sigReadDataEnd();
        return hasSize;
    }
    int size = hasSize > maxlen ?  maxlen:hasSize;
    int readPoint = _readPcmOil % _pcmBufferSize;

    int readSize1 = _pcmBufferSize - readPoint > size ? size:_pcmBufferSize - readPoint;
    memcpy(data,_pcmBuffer+readPoint,readSize1);
    if(readSize1 < size){
        int readSize2 = size - readSize1;
        memcpy(data+readSize1,_pcmBuffer,readSize2);
    }
    _readPcmOil += size;
    _pcmBufferMutex.unlock();
//    fillBuf();
    emit sigReadDataEnd();
    return size;
}
qint64 EZQAudioPlayer::writeData(const char */*data*/, qint64 /*len*/)
{
    return 0;
}
bool EZQAudioPlayer::willPlay() const
{
    return _willPlay;
}

void EZQAudioPlayer::setWillPlay(bool willPlay)
{
    _willPlay = willPlay;
}

EZQAudioPlayStatus EZQAudioPlayer::status() const
{
    return _status;
}

void EZQAudioPlayer::setStatus(const EZQAudioPlayStatus &status)
{
    _status = status;
    emit sigStausChanged(_status);
}
qint64 EZQAudioPlayer::currentTime()
{
    QMutexLocker lockerPlayer(&_playerMutex);
    qint64 time = 0;
    if(_player!=0){
        time = _player->processedUSecs();
    }
    return _currentTime + time;
}

void EZQAudioPlayer::setCurrentTime(const qint64 &currentTime)
{
    QMutexLocker lockerPlayer(&_playerMutex);
    if(_player){
        _stop(false);
        setStatus(EZQAudioPlayStatus_bufStart);
        _pcmBufferMutex.lock();
        _readPcmOil = 0;
        _writePcmOil = 0;
        _pcmBufferMutex.unlock();
        _currentTime = currentTime;
        _decode->setSeekPos(currentTime);
    }

}

float EZQAudioPlayer::volume() const
{
    return _volume;
}

void EZQAudioPlayer::setVolume(float volume)
{
    _volume = volume;
    QMutexLocker lockerPlayer(&_playerMutex);
    if(_player){
        _player->setVolume(volume);
    }
}
qint64 EZQAudioPlayer::duration()
{
    return _duration;
}
